/**
 * Licensed under the Apache License Version 2.0.
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package net.interknit.knitter.fw;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;

/**
 *
 */
public abstract class ServiceDelegate
{
	protected @Context javax.servlet.http.HttpServletRequest request;
	protected @Context javax.ws.rs.core.SecurityContext securityContext;
	private final java.util.Map<String, Object> invocationAttributes = new java.util.HashMap<String, Object>();
	private String description;

	protected ServiceDelegate()
	{
		CustomTypeFactory.getInstance().addCustomTypeClasses(getCustomTypes());
	}

	/**
	 * Sets the HTTPServletRequest, SecurityContext and Invocation attributes of
	 * the underlying service object. These values are collected from the
	 * invocation and dynamically set on the underlying object if that object
	 * defines the setter methods for these values. If the setter methods are
	 * not defined, the values will not be set. If defined, the setter methods
	 * must have the following signature:<br>
	 * <br>
	 * <code>
	 * public void setHttpServletRequest(javax.servlet.http.HttpServletRequest)<br>
	 * public void setSecurityContext(javax.ws.rs.core.SecurityContext)<br>
	 * public void setInvocationAttributes(Map<String, Object> invocationAttributes)
	 * </code>
	 *
	 * @param obj
	 *            Underlying service object reference.
	 */
	protected void setRestContext(Object obj)
	{
		try
		{
			java.lang.reflect.Method m = obj.getClass().getMethod("setHttpServletRequest", javax.servlet.http.HttpServletRequest.class);
			if(m != null) m.invoke(obj, this.request);
		}
		catch(Exception e)
		{
			// TODO make this a log statement
			System.out.println("Method " + obj.getClass().getName() + "." + "setHttpServletRequest(javax.servlet.http.HttpServletRequest)" +
					" does not exist. Cannot set the HttpServletRequest");
		}
		try
		{
			java.lang.reflect.Method m = obj.getClass().getMethod("setSecurityContext", javax.ws.rs.core.SecurityContext.class);
			if(m != null) m.invoke(obj, this.securityContext);
		}
		catch(Exception e)
		{
			// TODO make this a log statement
			System.out.println("Method " + obj.getClass().getName() + "." + "setSecurityContext(javax.ws.rs.core.SecurityContext)" +
					" does not exist. Cannot set the SecurityContext");
		}
		try
		{
			java.lang.reflect.Method m = obj.getClass().getMethod("setInvocationAttributes", java.util.Map.class);
			if(m != null) m.invoke(obj, this.invocationAttributes);
		}
		catch(Exception e)
		{
			// TODO make this a log statement
			System.out.println("Method " + obj.getClass().getName() + "." + "setInvocationAttributes(java.util.Map)" +
					" does not exist. Cannot set the Invocation Attributes");
		}
	}

	/**
	 * Sets the description of this service. These values are collected from the
	 * underlying object, by calling its describe() method, if available. The
	 * underlying object can optionally define the describe() method, which will
	 * be called by setDescription method to collect the description. The method
	 * must be defined with the following signature:<br>
	 * <br>
	 * <code>public String describe()</code><br>
	 *
	 * @param obj
	 *            Underlying service object reference.
	 */
	protected void setDescription(Object obj)
	{
		try
		{
			if(description == null)
			{
				java.lang.reflect.Method m = obj.getClass().getMethod("describe");
				if(m != null)
				{
					Object retValue = m.invoke(obj);
					if(retValue != null) description = String.valueOf(retValue);
					else description = null;
				}
			}
		}
		catch(Exception e)
		{
			// TODO make this a log statement
			System.out.println("Method " + obj.getClass().getName() + "." + "describe()" +
					" does not exist. Cannot get the description of this service");
		}
	}

	@GET
	@Produces("text/xml")
	@Path("/layout/xml")
	public java.lang.String layout()
	{
		try
		{
			java.io.InputStream is = getClass().getResourceAsStream("endpoint.xml");

			java.lang.StringBuilder sb = new java.lang.StringBuilder();

			java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(is));
			for(; ;)
			{
				java.lang.String line = br.readLine();
				if(line != null)
				{
					sb.append(line).append("\n");
				}
				else break;
			}

			String layout = sb.toString();

			if(! layout.isEmpty())
			{
				String baseAddress = System.getProperty("base.endpoint.address");

				if(baseAddress == null || "".equals(baseAddress))
				{
					String host = request.getServerName();
					int port = request.getServerPort();
					baseAddress = host + (port == 80 ? "" : (":" + String.valueOf(port)));

					baseAddress = "http" + (request.isSecure() ? "s" : "") + "://" +  baseAddress;
				}

				layout = layout.replaceAll("BASE_ENDPOINT_ADDRESS", baseAddress);
			}

			return layout;
		}
		catch(java.lang.Exception e)
		{
			throw (e instanceof WebApplicationException ? ((WebApplicationException) e) : new WebApplicationException(e));
		}
	}

	@GET
	@Produces("text/xml")
	@Path("/layout/apidoc")
	public java.lang.String describe()
	{
		return description != null ? description : ("<error>description not available for this service</error>");
	}

	protected abstract Class<?>[] getCustomTypes();
}
